package com;

/**
 * 比较经典的面试题
 * 类似于上面的奇数偶数,通过 wait 和 notify 实现
 * 
 * 思路:
 * 1.有一个业务类 SynStack 其中有一个整数成员变量 cnt 用来保存生产的个数,还需要有一个char[] 数组来保存生产的元素(a,b,c...)
 * 2.业务类提供生产方法和消费方法
 * 		1.生产 push :判断数组是否满了,满了就 wait(),就不在生产,唤醒消费者线程,如果没满,就向数组中添加数据,cnt++
 * 		2.消费 pop : 同上,只是判断数组是否为空,cnt--
 * 3.两个线程类,分别去循环调用pop 和 push 两个方法,两个线程需要有一个成员变量 SynStack,并且两个线程需要保存同一个对象
 * 
 * @author lenovo
 * @date 2020年7月25日
 */
public class Thread_02_ProducerConsumer {

	public static void main(String[] args) {
		SynStack ss = new SynStack();
		Thread t1 = new Thread(new Producer(ss));
		Thread t2 = new Thread(new Consumer(ss));

		t1.start();
		t2.start();
	}

}

//生产线程
class Producer implements Runnable {
	SynStack ss = null;

	public Producer(SynStack ss) {
		super();
		this.ss = ss;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			char ch = (char) ('a' + i);
			ss.push(ch);
		}
	}

}

//消费线程
class Consumer implements Runnable {
	SynStack ss = null;

	public Consumer(SynStack ss) {
		super();
		this.ss = ss;
	}

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			ss.pop();
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

}

class SynStack {
	// 已保存的个数,同时也是下一个元素的下标
	int cnt = 0;
	char[] data = new char[6];

	public synchronized void push(char ch) {
		if (cnt == data.length) {
			try {
				// 这里不用加 notifyAll, 因为消费者是消费完之后再等待的,而这里是生产满
				// 所以不需要在这里 唤醒
				// 如果在这里唤醒,比如消费者消费完了,进入等待,并且是在这里唤醒消费者的话
				// 就变成了生产满之后,在唤醒消费
				// 这样不合适,应该哪怕只有一件商品,也要唤醒你去消费
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notifyAll();
		data[cnt] = ch;
		cnt++;
		System.out.println("生产了 : " + ch + "剩余 : " + cnt + "个元素");
	}

	public synchronized char pop() {
		// 因为 cnt 是已有元素的个数,所以 cnt 是不能等于 0 的,等于0说明数组中没有东西
		if (cnt == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.notifyAll();
		// 先-- 后取出 ,因为 cnt 是已有元素个数,最后一个下标就是 从 cnt-1
		cnt--;
		char ch = data[cnt];
		System.out.println("取出了 : " + ch + "剩余 : " + cnt + "元素");
		return ch;
	}
}